/*
 * fitness.cpp
 *
 * Copyright (C) 2008, 2009
 *
 * Lukasz Bieniasz-Krzywiec
 * Dariusz Leniowski
 */

#include "fitness.h"
#include <map>
#include <cmath>

/* fitness staly jako klasa bazowa dla fitnessow */
void fitness::oblicz()
{
  int wierzcholek;
  #pragma omp parallel for private(wierzcholek)
  FOREN(wierzcholek) {
	populacja[wierzcholek]->fitness = 1.0;
	fitness_osobnik[wierzcholek] = wierzcholek;
  }
  shuffle(fitness_osobnik, fitness_osobnik + N);
  #pragma omp parallel for private(wierzcholek)
  FOREN(wierzcholek) osobnik_fitness[fitness_osobnik[wierzcholek]] = wierzcholek;
}

/* Komparator pomocniczy dla funkcji sort */
bool fitness::cmp(int a, int b)
{
  ASSERT(a>=0 && a<N && b >=0 && b < N);
  return populacja[a]->fitness > populacja[b]->fitness;
}

/* Permutuje losowo tablice zaczynajaca sie w a
 * o rozmiarze b-a
 */
void fitness::shuffle(int *a, int *b)
{
  int ii;
  for(ii = b - a; ii >= 2; -- ii) {
    int l = r->rand(ii);
    swap(a[l], a[ii - 1]);
  }
}

/* Permutuje losowo osobniki o takim samym fitnessie
 * Zalozenia:
 * - permutacja fitness_osobnik jest posortowana
 *   wzgledem fitnessu
 */
void fitness::randomize()
{
  int ii = 0, jj = 0, end = 0;
  #pragma omp parallel private(ii, jj, end)
  {
    // dzielimy tablice na num_threads czesci
	ii = jj = N*omp_get_thread_num()/omp_get_num_threads();
    end = N*(omp_get_thread_num()+1)/omp_get_num_threads();
	// sprawdzamy, czy mozemy zaczac od poczatku
	if (jj > 0 && populacja[fitness_osobnik[jj]]->fitness ==
		   	populacja[fitness_osobnik[jj-1]]->fitness) {
	  // nie, nie mozemy, wiec szukamy pierwszego miejsca,
	  // na ktorym populacja sie rozni wg fitnessu
	  ii = jj;
      while (jj < N && populacja[fitness_osobnik[ii]]->fitness ==
        populacja[fitness_osobnik[jj]]->fitness) ++ jj;
    }
    while (jj < end) {
	  ii = jj;
  	  while (jj < N && populacja[fitness_osobnik[ii]]->fitness ==
        populacja[fitness_osobnik[jj]]->fitness) {
		  ++ jj;
	  }
      shuffle(fitness_osobnik + ii, fitness_osobnik + jj);
  	} 
  }
}

/* Wylaczenie wspolnego kodu:
 * - posortowanie wzgledem fitnessu
 * - przetasowanie w ramach tego samego fitnessu
 * - odwrocenie permutacji fitness_osobnik
 *   do osobnik_fitness
 */
void fitness::sort_randomize_reverse()
{
  int wierzcholek;
  sort(fitness_osobnik, fitness_osobnik + N, cmp);
  randomize();
  #pragma omp parallel for private(wierzcholek)
  FOREN(wierzcholek) osobnik_fitness[fitness_osobnik[wierzcholek]] = wierzcholek; // odwroc permutacje
}

/*
 * Przystosowanie opierajace sie na ostatniej wyplacie
 */

fitness_ostatnia_wyplata::fitness_ostatnia_wyplata() {}
fitness_ostatnia_wyplata::~fitness_ostatnia_wyplata() {}

/*
 * Przelicz przystosowanie wszystkich osobnikow
 * Dodatkowe zalozenia:
 * - Kazdy osobnik mial jakas wyplate
 */
void fitness_ostatnia_wyplata::oblicz()
{
  int wierzcholek;
  #pragma omp parallel for private(wierzcholek)
  FOREN(wierzcholek) {
    populacja[wierzcholek]->fitness = populacja[wierzcholek]->wyplata; // ustaw fitness na ostatnia wyplate
	ASSERT(finite(populacja[wierzcholek]->fitness));
    fitness_osobnik[wierzcholek] = wierzcholek; // wyzeruj permutacje
  }
  sort_randomize_reverse();
}

/* suma wyplat podzielona przez stopien (sume wag krawedzi wychodzacych) */
fitness_ostatnia_wyplata_znormalizowana::
  fitness_ostatnia_wyplata_znormalizowana() {}
fitness_ostatnia_wyplata_znormalizowana::
  ~fitness_ostatnia_wyplata_znormalizowana() {}

void fitness_ostatnia_wyplata_znormalizowana::oblicz() {
  int wierzcholek;
  real stopien;
  #pragma omp parallel for private(wierzcholek, stopien)
  FOREN(wierzcholek) {
    stopien = populacja[wierzcholek]->stopien_wierzcholka();
    // ustaw fitness na ostatnia wyplate znormalizowana
    // lub 0 jezeli stopien jest 0
    populacja[wierzcholek]->fitness = stopien != 0.0 ? populacja[wierzcholek]->wyplata / stopien : 0.0;
	ASSERT(finite(populacja[wierzcholek]->fitness));
    fitness_osobnik[wierzcholek] = wierzcholek; // wyzeruj permutacje
  }
  sort_randomize_reverse();
}


/* liczebnosc rodzaju do ktorego nalezy osobnik */
fitness_liczebnosc_rodzaju::fitness_liczebnosc_rodzaju() {}
fitness_liczebnosc_rodzaju::~fitness_liczebnosc_rodzaju() {}

void fitness_liczebnosc_rodzaju::oblicz() {
  int wierzcholek;
  map<int, int> liczebnosc_rodzaju;
  FOREN(wierzcholek) {
    liczebnosc_rodzaju[populacja[wierzcholek]->rodzaj] = liczebnosc_rodzaju[populacja[wierzcholek]->rodzaj] + 1;
  }
  #pragma omp parallel for private(wierzcholek)
  FOREN(wierzcholek) {
    populacja[wierzcholek]->fitness = liczebnosc_rodzaju[populacja[wierzcholek]->rodzaj];
	ASSERT(finite(populacja[wierzcholek]->fitness));
    fitness_osobnik[wierzcholek] = wierzcholek; // zerowanie permutacji
  }
  sort_randomize_reverse();
}


/* suma wyplat osobnikow z danego rodzaju */
fitness_wyplata_rodzaju::fitness_wyplata_rodzaju() {}
fitness_wyplata_rodzaju::~fitness_wyplata_rodzaju() {}
void fitness_wyplata_rodzaju::oblicz() {
  int wierzcholek;
  map<int, real> wyplata_rodzaju;
  FOREN(wierzcholek) {
    wyplata_rodzaju[populacja[wierzcholek]->rodzaj] =
      wyplata_rodzaju[populacja[wierzcholek]->rodzaj]
	  + populacja[wierzcholek]->wyplata;
  }
  #pragma omp parallel for private(wierzcholek)
  FOREN(wierzcholek) {
    populacja[wierzcholek]->fitness = wyplata_rodzaju[populacja[wierzcholek]->rodzaj] / rodz[populacja[wierzcholek]->rodzaj];
	ASSERT(finite(populacja[wierzcholek]->fitness));
    fitness_osobnik[wierzcholek] = wierzcholek; // zerowanie permutacji
  }
  sort_randomize_reverse();
}

/* suma znormalizowanych wyplat osobnikow z danego rodzaju */
fitness_wyplata_znormalizowana_rodzaju::
  fitness_wyplata_znormalizowana_rodzaju() {}
fitness_wyplata_znormalizowana_rodzaju::
  ~fitness_wyplata_znormalizowana_rodzaju() {}

void fitness_wyplata_znormalizowana_rodzaju::oblicz() {
  int wierzcholek;
  map<int, real> wyplata_rodzaju;
  FOREN(wierzcholek) {
    real stopien = populacja[wierzcholek]->stopien_wierzcholka();
    if (stopien != 0.0) {
      wyplata_rodzaju[populacja[wierzcholek]->rodzaj] =
		  wyplata_rodzaju[populacja[wierzcholek]->rodzaj] + populacja[wierzcholek]->wyplata / stopien;
    }
  }
  #pragma omp parallel for private(wierzcholek)
  FOREN(wierzcholek) {
    populacja[wierzcholek]->fitness = wyplata_rodzaju[populacja[wierzcholek]->rodzaj] / rodz[populacja[wierzcholek]->rodzaj];
	ASSERT(finite(populacja[wierzcholek]->fitness));
    fitness_osobnik[wierzcholek] = wierzcholek; // zerowanie permutacji
  }
  sort_randomize_reverse();
}

